package yartiss.engine.simulation.scheduling.policies.dp;

import java.util.SortedSet;

import yartiss.engine.simulation.Event;
import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.ISchedulingPolicy;
import yartiss.engine.simulation.events.EventGenerator;
import yartiss.engine.simulation.scheduling.policies.AbstractMonoProcessorSchedulingPolicy;
import yartiss.engine.simulation.scheduling.policies.tools.DPSlacktimeManager;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.engine.simulation.system.taskset.TaskSetFactory;


/**
 * Preemptive Dynamic Priority. When there is not enough energy, the system is
 * paused for a duration equal to the slack time. However if there is no
 * slack time, the system is paused 1 time unit. (And will probably fail).
 * Moreover, if energy state reaches Emax, the schedule is resumed
 */
public class EHPDP4SchedulingPolicy extends AbstractMonoProcessorSchedulingPolicy  {

	private final DPSlacktimeManager slacktimeManager;
	
	public EHPDP4SchedulingPolicy(DPSlacktimeManager slacktimeManager){
		this.slacktimeManager = slacktimeManager;
		
	}

	@Override
	public String getPolicyName() {
		return "EH_PDP_4";
	}

	@Override
	public ITaskSet createTaskSet() {
		return TaskSetFactory.getNewInstance("deadline");
	}

	long sleepUntil = -1;
	
	@Override
	public ITask chooseNextTask(ITaskSet taskSet, IEnergyProfile energyProfile, long date, EventGenerator evGen) {
		getMetrics().clear();
		if (date == 0)
			sleepUntil = -1;

		SortedSet<ITask> activeTasks = taskSet.getActiveTasks(date);

		if (!activeTasks.isEmpty() && date >= sleepUntil) {
			ITask first = activeTasks.first();
			if (date == sleepUntil && energyProfile.getCurrentEnergyLevel() < energyProfile.getMaxEnergyLevel()) {
				long st = slacktimeManager.getSlackTime(date, taskSet);
				getMetrics().put(Event.SlackTime, st);
				if (st > 0) {
					long x = Math.max(st, 1);
					long de = energyProfile.getMaxEnergyLevel()	- energyProfile.getCurrentEnergyLevel();
					long dt = de / energyProfile.getPowerRate();

					x = Math.min(x, dt);

					sleepUntil = date + x;
					if (date != sleepUntil) {
						evGen.generateEvent("check_energy_state",0, activeTasks.first(), sleepUntil, null);
						return null;
					}
				}
			}

			long hlcet = energyProfile.howLongCanExecute(first);

			if (hlcet <= 0) {

				long st = slacktimeManager.getSlackTime(date, taskSet);
				getMetrics().put(Event.SlackTime, st);
				long x = Math.max(st, 1);
				long de = energyProfile.getMaxEnergyLevel()	- energyProfile.getCurrentEnergyLevel();
				long dt = de / energyProfile.getPowerRate();
				if (dt == 0)
					dt = 1;
				x = Math.min(x, dt);

				sleepUntil = date + x;

				evGen.generateEvent("check_energy_state",0, activeTasks.first(), sleepUntil, null);
				return null;
			}

			if (hlcet < activeTasks.first().getRemainingCost())
				evGen.generateEvent("check_energy_state",0, activeTasks.first(),date + hlcet, null);

			return activeTasks.first();
		}

		return null;
	}
	
	@Override
	public ISchedulingPolicy newInstance() {
		return new EHPDP4SchedulingPolicy(new DPSlacktimeManager());
	}
}
